#pragma once
#include <iostream>
#include <unistd.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <cstring>
#define CONV(addr) ((struct sockaddr*)&addr)

class Socket
{
public:
    Socket() {}
    ~Socket() {}
    virtual void CreateSocket() = 0;
    virtual void BindSocket(uint16_t port) = 0;
    virtual void ListenSOcketOrDie(int backlog) = 0;
    virtual int AcceptConnection(std::string *peerip, uint16_t *peerport) = 0;
    virtual bool ConnectServer(const std::string &peerip, uint16_t peerport) = 0;
    virtual int GetSocket() = 0;
    virtual void SetSocket(int sockfd) = 0;
    virtual void CloseSocket() = 0;
    virtual bool Recv(std::string *out, size_t size) = 0;
    virtual void Send(const std::string &in) = 0;

public:
    void BuiltListenSockfd(uint16_t port, int backlog)
    {
        CreateSocket();
        BindSocket(port);
        ListenSOcketOrDie(backlog);
    }

    bool BuildConnectSocket(const std::string &peerip, uint16_t peerport)
    {
        CreateSocket();
        return ConnectServer(peerip, peerport);
    }
    void BuiltNormalSocket(int sockfd)
    {
        SetSocket(sockfd);
    }
};

class TcpSocket : public Socket
{
public:
    virtual void CreateSocket()
    {
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if(_sockfd < 0) exit(0);
        // 设置非阻塞IO ... TODO
    }
    virtual void BindSocket(uint16_t port)
    {
        struct sockaddr_in local;
        local.sin_family = AF_INET;
        local.sin_port = htons(port);
        local.sin_addr.s_addr = INADDR_ANY;
        int n = ::bind(_sockfd, CONV(local), sizeof(local));
        if(n < 0){
            exit(0);
        }
    }
    virtual void ListenSOcketOrDie(int backlog)
    {
        int n = listen(_sockfd, backlog);
        if(n < 0) exit(0);
    }
    virtual int AcceptConnection(std::string *peerip, uint16_t *peerport)
    {
        struct sockaddr_in svr;
        socklen_t sz = sizeof(svr);
        int sockfd = accept(_sockfd, CONV(svr), &sz);
        if(sockfd < 0) return -1;
        char buffer[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, &svr.sin_addr.s_addr, buffer, sizeof(buffer));
        *peerport = ntohs(svr.sin_port);
        *peerip = buffer;
        return sockfd;
    }
    virtual bool ConnectServer(const std::string &peerip, uint16_t peerport)
    {
        struct sockaddr_in svr;
        memset(&svr, 0, sizeof(svr));
        inet_pton(AF_INET, peerip.c_str(), &svr.sin_addr.s_addr);
        svr.sin_port = htons(peerport);
        svr.sin_family = AF_INET;
        int n = connect(_sockfd, CONV(svr), sizeof(svr));
        return n == 0;
    }
    virtual int GetSocket()
    {
        return _sockfd;
    }
    virtual void SetSocket(int sockfd)
    {
        _sockfd = sockfd;
    }
    virtual void CloseSocket()
    {
        if(_sockfd >= 0)
            close(_sockfd);
    }
    virtual bool Recv(std::string *out, size_t size)
    {
        char buffer[1024];
        int n = recv(_sockfd, buffer, sizeof(buffer) - 1, 0);
        if(n > 0)
        {
            buffer[n] = 0;
            *out += buffer;
            return true;
        }
        return false;
    }
    virtual void Send(const std::string &in)
    {
        send(_sockfd, in.c_str(), in.size(), 0);
    }

private:
    int _sockfd;
};